using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration;
using System.Data;
using System.IO;
using System.Runtime.InteropServices;
using System.Security;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml;
using System.Xml.XPath;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint.WebPartPages;
using Microsoft.SharePoint.WebPartPages.Communication;
using PS.WebParts.Rollup;
using PS.WebParts.Rollup.Core;
using WebPart=Microsoft.SharePoint.WebPartPages.WebPart;
using System.Xml.Serialization;

namespace PS.WebParts.Export
{
    [Guid("6174438f-6ba6-4757-9bc3-7a4b4971136f")]
    [XmlRoot(Namespace = "PS.WebParts.Rollup")]
    public class ExcelPart : WebPart, IListConsumer
    {
        private const string CReloadMessage = @"The page will refresh shortly.";
        private const string DefaultStyle = "";
        private const string DefaultText = "Export Report";
        private const string DefaultTemplateUrl = "http://";
        private bool clicked = false;
        private Button startBtn;
        private XmlDocument xdoc = null;

        public ExcelPart()
        {
            ButtonStyle = DefaultStyle;
            ButtonText = DefaultText;
            TemplatePath = DefaultTemplateUrl;
            ExplicitTypes = "";
            ShowOutput = false;
        }

        [Category("Excel Settings"), WebPartStorage(Storage.Personal), DefaultValue(false),
         FriendlyName("Show Text Output"), Description("If selected, then the text output will be rendered."),
         Personalizable(), WebBrowsable(true)]
        public bool ShowOutput { get; set; }


        [Category("Excel Settings"), WebPartStorage(Storage.Personal), DefaultValue(""), FriendlyName("Field Types"),
         Description(
             "Explicitly specify field types in the format:FIELDNAME=FIELDTYPE;....Types: bool, number, text, datetime")
        , Personalizable(), WebBrowsable(true)]
        public string ExplicitTypes { get; set; }


        [Category("Excel Settings"), WebPartStorage(Storage.Personal), DefaultValue(DefaultTemplateUrl),
         FriendlyName("Template Path"), Description("The location of a template in Xml Spreadsheet 2003 format."),
         Personalizable(), WebBrowsable(true)]
        public string TemplatePath { get; set; }


        [Category("Excel Settings"), WebPartStorage(Storage.Personal), DefaultValue(DefaultText),
         FriendlyName("Button Text"), Description("The text that will be displayed on the button."), Personalizable(),
         WebBrowsable(true)]
        public string ButtonText { get; set; }


        [Category("Excel Settings"), WebPartStorage(Storage.Personal), DefaultValue(DefaultStyle),
         FriendlyName("Button Style"), Description("Value of the style attribute that will be rendered for the button.")
        , Personalizable(), WebBrowsable(true)]
        public string ButtonStyle { get; set; }

        #region ImplementConsumer

        private bool _connected = false;
        private string _connectedWebPartLabel = "Connected to Web Part";
        private string _connectedWebPartTitle = string.Empty;
        private string _notConnectedMsg = "NOT CONNECTED. To use this Web Part connect it to a List Provider Web Part.";
        private string _registrationErrorMsg = "An error has occurred trying to register your connection interfaces.";
        private bool _registrationErrorOccurred = false;


       
        private DataTable tableIn = new DataTable();

        public void ListProviderInit(object sender, ListProviderInitEventArgs listProviderInitEventArgs)
        {
        }

        public void ListReady(object sender, ListReadyEventArgs listReadyEventArgs)
        {
            if (!clicked)
                return;

            // Ensure that all of the Web Part's controls are created.
            EnsureChildControls();

            // Store the List that was passed by the provider Web Part. 
            if (listReadyEventArgs.List != null)
            {
                var dv = new DataView(listReadyEventArgs.List);

                try
                {
                    var df = sender as RollUpDataFormWebPart;

                    if (df != null)
                    {
                        Dictionary<string, string> param = df.GetFilter();

                        if (param.ContainsKey("filterfield") && !string.IsNullOrEmpty(param["filterfield"]) &&
                            param.ContainsKey("filterval"))
                        {
                            string filterfeild = param["filterfield"];

                            if (filterfeild.StartsWith("@"))
                                filterfeild = filterfeild.Remove(0, 1);

                            string filterVal = param["filterval"];
                            if (filterVal == null)
                                filterVal = string.Empty;

                            DataColumn col = dv.Table.Columns[filterfeild];

                            object o = col != null ? col.ExtendedProperties[FieldInfo.ExtPropKey] : null;

                            SPFieldType t = o != null ? (SPFieldType) o : SPFieldType.Invalid;

                            try
                            {
                                if (t == SPFieldType.DateTime)
                                {
                                    filterVal = filterVal.Substring(0, filterVal.IndexOf('T'));
                                    dv.RowFilter = filterfeild + " LIKE '" + filterVal + "*'";
                                }
                                else
                                    dv.RowFilter = filterfeild + " = '" + filterVal + "'";
                            }
                            catch
                            {
                                if (filterfeild.Contains("_x005F_x0020_"))
                                {
                                    dv.RowFilter = filterfeild.Replace("_x005F_x0020_", "_x0020_") + " = '" + filterVal +
                                                   "'";
                                }
                                else
                                    throw;
                            }
                        }


                        if (param.ContainsKey("sortfield") && !string.IsNullOrEmpty(param["sortfield"]))
                        {
                            string sortfield = param["sortfield"];
                            if (sortfield.StartsWith("@"))
                                sortfield = sortfield.Remove(0, 1);

                            DataColumn col = dv.Table.Columns[sortfield];

                            object o = col != null ? col.ExtendedProperties[FieldInfo.ExtPropKey] : null;

                            SPFieldType t = o != null ? (SPFieldType) o : SPFieldType.Invalid;

                            bool isSortTypeDate = t == SPFieldType.DateTime;
                            bool isSortTypeNumber = t == SPFieldType.Number || t == SPFieldType.Integer ||
                                                    t == SPFieldType.Currency;

                            //if (param.ContainsKey("sorttype") && !string.IsNullOrEmpty(param["sorttype"]) &&
                            //    (param["sorttype"].Trim().ToLower() != "text"))

                            if (isSortTypeNumber)
                            {
                                //string sorttype = param["sorttype"].Trim().ToLower();

                                //if (sorttype == "number")
                                //{
                                string numColumn = sortfield + "NUM";

                                //   tableIn.Columns[sortfield].ColumnName += "1";

                                dv.Table.Columns.Add(numColumn, typeof (Decimal));

                                for (int i = 0; i < dv.Table.Rows.Count; i++)
                                {
                                    DataRow dr = dv.Table.Rows[i];


                                    decimal nr;
                                    if (decimal.TryParse(dr[sortfield].ToString(), out nr))
                                        dr[numColumn] = nr; // decimal.Parse(dr[sortfield].ToString());
                                }

                                dv.Table.Columns.Remove(sortfield);
                                dv.Table.Columns[numColumn].ColumnName = sortfield;
                              
                            }

                            dv.Sort = sortfield;

                            if (param.ContainsKey("sortdir") && !string.IsNullOrEmpty(param["sortdir"]) &&
                                param["sortdir"].Trim().ToLower() == "descending")
                            {
                                dv.Sort += " DESC";
                            }
                        }
                    }
                }
                catch
                {
                }

                tableIn = dv.ToTable();
            }
        }


        public void PartialListReady(object sender, PartialListReadyEventArgs partialListReadyEventArgs)
        {
        }

        public override void EnsureInterfaces()
        {
          
            try
            {
              
                RegisterInterface("MyListConsumerInterface", //InterfaceName    
                                  InterfaceTypes.IListConsumer, //InterfaceType
                                  LimitOneConnection, //MaxConnections
                                  ConnectionRunAt.Server, //RunAtOptions
                                  this, //InterfaceObject
                                  "", //InterfaceClientReference
                                  "Consume List From", //MenuLabel
                                  "Consumes a list from another Web Part."); //Description
            }
            catch (SecurityException)
            {
                _registrationErrorOccurred = true;
            }
        }


        public override ConnectionRunAt CanRunAt()
        {
            return ConnectionRunAt.Server;
        }


        public override void PartCommunicationConnect(string interfaceName,
                                                      WebPart connectedPart,
                                                      string connectedInterfaceName,
                                                      ConnectionRunAt runAt)
        {
            // Keep track of connection state.
            if (interfaceName == "MyListConsumerInterface")
            {
                _connected = true;
                _connectedWebPartTitle = SPEncode.HtmlEncode(connectedPart.Title);
            }
        }

        #endregion

        internal string OpenTemplate(string fpath)
        {
            string tempPath = TemplatePath;

            if (tempPath == DefaultTemplateUrl)
                return DefaultTemplateUrl;

            try
            {
                if (!SPUrlUtility.IsUrlFull(tempPath))
                {
                    tempPath = SPUtility.GetFullUrl(SPControl.GetContextSite(Context), tempPath);
                }

                string lib = tempPath.Trim();


                string lib1Mod = lib.Remove(lib.LastIndexOf("/"));
                int site1Del = lib1Mod.LastIndexOf("/");
                string site1Url = lib1Mod.Substring(0, site1Del);

                using (var site = new SPSite(site1Url))
                {
                    using (SPWeb web = site.OpenWeb())
                    {
                        SPFile file = web.GetFile(tempPath);

                        if (file != null)
                        {
                            Stream stream = file.OpenBinaryStream();
                            xdoc = new XmlDocument();
                            xdoc.Load(stream);
                        }
                    }
                }
            }
            catch
            {
            }

            return tempPath;
        }

        internal Dictionary<string, string> ParsedExTypes()
        {
            var ret = new Dictionary<string, string>();
            try
            {
                string[] rawTypes = ExplicitTypes.Split(';');

                foreach (string s in rawTypes)
                {
                    string type = s.Trim();

                    string[] value = type.Split('=');

                    if (value.Length == 2)
                    {
                        if (!ret.ContainsKey(value[0].Trim()))
                            ret.Add(value[0].Trim(), value[1].Trim().ToLower());
                    }
                }
            }
            catch
            {
            }
            return ret;
        }

        internal bool SendRepsonse()
        {
            bool success = false;
            HttpResponse response = Context.Response;
            try
            {
              

                response.Clear();

                response.ContentType = "application/ms-excel";

                response.AddHeader("Content-Disposition",
                                   "attachment;filename=" +
                                   HttpUtility.UrlEncode(_connectedWebPartTitle + DateTime.Now.ToString("s")) + " .xml");

                 MemoryStream ms = new MemoryStream();
                xdoc.Save(ms);
                response.BinaryWrite(ms.ToArray());
               
                //response.End();
                response.Flush();
                response.Close();
              
                success = true;
            }
            catch(System.Exception exception)
            {
                string ex = exception.Message;
            }
            
             //  HttpContext.Current.ApplicationInstance.CompleteRequest();
            //  response.Flush();
              //  response.
            
            return success;

        }

        private static string StripHTML(string htmlString)
        {
            string pattern = @"<(.|\n)*?>";

            return Regex.Replace(htmlString, pattern, string.Empty);
        }

        private bool ModifyTemplate(XmlNode repeatingNode)
        {
            bool success = false;

            try
            {
                XPathNavigator nav = repeatingNode.CreateNavigator();

                Dictionary<string, string> guessedTypes = Util.GuessTypes(tableIn);
                Dictionary<string, string> masterTypes;

               
                masterTypes = new Dictionary<string, string>(tableIn.Columns.Count);
                foreach (DataColumn col in tableIn.Columns)
                {
                    if (col.ExtendedProperties.ContainsKey(FieldInfo.ExtPropKey))
                    {
                        masterTypes.Add(col.ColumnName, col.ExtendedProperties[FieldInfo.ExtPropKey].ToString());
                    }
                }

                foreach (string s in guessedTypes.Keys)
                {
                    if (!masterTypes.ContainsKey(s))
                        masterTypes.Add(s, guessedTypes[s]);
                }

                foreach (DataRow dr in tableIn.Rows)
                {
                    XmlNode insert = repeatingNode.Clone();
                    XPathNavigator navi = insert.CreateNavigator();
                    XPathNodeIterator xni = navi.SelectDescendants("Data",
                                                                   "urn:schemas-microsoft-com:office:spreadsheet", false);

                    while (xni.MoveNext())
                    {
                        string value = xni.Current.Value;
                        if (value.Contains("{{") && value.Contains("}}"))
                        {
                            string fieldraw = value.Substring(value.IndexOf("{{") + 2,
                                                              value.IndexOf("}}") - value.IndexOf("{{") - 2);

                            string field = fieldraw.Trim();

                            if (tableIn.Columns.Contains(field))
                            {
                                value = value.Replace("{{" + fieldraw + "}}", dr[field].ToString());

                                if (value.Contains("<") && value.Contains(">"))
                                    value = StripHTML(value);

                                if (value.Contains("<div>"))
                                    value = value.Replace("<div>", "");

                                if (value.Contains("</div>"))
                                    value = value.Replace("</div>", "");

                                if (value.Contains("&nbsp;"))
                                    value = value.Replace("&nbsp;", "");

                                if (!string.IsNullOrEmpty(value) && masterTypes.ContainsKey(field))
                                {
                                    string type = masterTypes[field];
                                    if (type != null) type = type.ToLower();

                                    switch (type)
                                    {
                                        case "number":
                                        case "currency":
                                        case "integer":
                                            XPathNavigator typeAtt = xni.Current.Clone();

                                            typeAtt.MoveToAttribute("Type",
                                                                    "urn:schemas-microsoft-com:office:spreadsheet");

                                            typeAtt.SetValue("Number");

                                            break;
                                        case "datetime":
                                            typeAtt = xni.Current.Clone();

                                            typeAtt.MoveToAttribute("Type",
                                                                    "urn:schemas-microsoft-com:office:spreadsheet");

                                            typeAtt.SetValue("DateTime");

                                            if (!value.Contains("T"))
                                            {
                                                DateTime dt;
                                                if (DateTime.TryParse(value, out dt))
                                                {
                                                    value = XmlConvert.ToString(dt,
                                                                                XmlDateTimeSerializationMode.Unspecified);
                                                }
                                            }

                                            break;

                                        case "bool":
                                        case "boolean":
                                            if (value.Trim() == "0")
                                                value = "No";
                                            else if (value.Trim() == "1")
                                                value = "Yes";

                                            break;
                                    }

                                    xni.Current.SetValue(value);
                                }
                                else if (!string.IsNullOrEmpty(value))
                                {
                                    xni.Current.SetValue(value);
                                }
                                else
                                {
                                    xni.Current.DeleteSelf();
                                }
                            }
                        }
                    }


                    nav.InsertBefore(navi);
                }

                XmlNode nodep = repeatingNode.ParentNode;

                nodep.Attributes["ExpandedRowCount", "urn:schemas-microsoft-com:office:spreadsheet"].Value =
                    (int.Parse(
                         nodep.Attributes["ExpandedRowCount", "urn:schemas-microsoft-com:office:spreadsheet"].Value) +
                     tableIn.Rows.Count - 1).ToString();

                nav.DeleteSelf();


                success = true;
            }
            catch
            {
            }

            return success;
        }

        protected override void RenderWebPart(HtmlTextWriter output)
        {
            // Check for connection interface registration error
            if (_registrationErrorOccurred)
            {
                if (ShowOutput)
                    output.Write(_registrationErrorMsg);
                return;
            }

            // Ensure that all of the Web Part's controls are created.
            EnsureChildControls();

            // Check if connected.
            if (_connected)
            {
                if (tableIn != null && clicked)
                {
                    try
                    {
                        string pathUsed = OpenTemplate(TemplatePath);

                        if (xdoc != null)
                        {
                            XmlNodeList xnl = xdoc.GetElementsByTagName("Row");

                            for (int i = 0; i < xnl.Count; i++)
                            {
                                XmlNode n = xnl[i];

                                if (n.InnerXml.Contains("{{") && n.InnerXml.Contains("}}"))
                                {
                                    if (!ModifyTemplate(n))
                                    {
                                        if (ShowOutput)
                                            output.Write("Error modifying template.");
                                        return;
                                    }

                                    break;
                                }
                            }


                            if (!SendRepsonse())
                                if (ShowOutput)
                                    output.Write("Error outputing file.  ");
                         
                        }
                        else
                        {
                            if (pathUsed == DefaultTemplateUrl)
                                output.Write("Please specify a template location in the web part's properties.");
                            else
                                output.Write("Error retrieving template.  Specified location: " + pathUsed);


                            return;
                        }
                        clicked = false;
                    }
                    catch (Exception ex)
                   {
                        if (ShowOutput)
                           output.Write(ex.Message);
                   }
                }

                // Render connected Web Part title.
                if (ShowOutput)
                {
                    output.Write(_connectedWebPartLabel + ": ");
                    output.RenderBeginTag(HtmlTextWriterTag.I);
                    output.Write(_connectedWebPartTitle);
                    output.RenderEndTag();
                    output.Write("<br>");
                }

                if (ButtonStyle != "")
                    startBtn.Style.Value = ButtonStyle;

                HttpContext conttext = base.Context;
                

                startBtn.RenderControl(output);

                string reloadMessage =
                    ConfigurationManager.AppSettings["PS.WebParts.Rollup.ExcelExport.PageRefreshText"];

                if (string.IsNullOrEmpty(reloadMessage))
                    reloadMessage = CReloadMessage;
                output.Write("<div style='margin-top:10px; display:none' id='msg_" + startBtn.UniqueID + "'>" +
                             string.Format(reloadMessage, conttext.Request.Url) + "</div>");

            }
            else
            {
                // The Web Part isn't connected.
                if (ShowOutput)
                    output.Write(_notConnectedMsg);
            }
        }

        // Create Web Part user interface controls.

        protected override void CreateChildControls()
        {
            startBtn = new Button();

            startBtn.Text = ButtonText;
            startBtn.Style.Value = ButtonStyle;
            startBtn.Click += StartBtnClick;

            //must have this to fix client events
            startBtn.OnClientClick = "this.form.onsubmit = function() {return true;}";

            Controls.Add(startBtn);
        }

        private void StartBtnClick(object sender, EventArgs e)
        {
            clicked = true;
        }
    }
}